home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
boot
/
czesc_2
/
wiconify
/
wiconsetter.lzh
/
wIconCutter
/
Frame.c
next >
Wrap
C/C++ Source or Header
|
1991-04-19
|
21KB
|
730 lines
/*
* FRAME.C A general-purpose bitmap creating library. It allows you
* to select a portion of any screen by dragging a selection
* rectangle, and returns a bitmap structure that contains
* a copy of the selected region.
*
* Copyright 1990 by Davide P. Cervone, all rights reserved.
* You may use this code, provided this copyright notice is kept intact.
*/
#define INTUITIONPRIVATE TRUE
#include <intuition/intuitionbase.h>
#include <exec/interrupts.h>
#include <exec/memory.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <libraries/dos.h>
#include "Frame.h"
struct LayersBase *LayersBase;
struct Screen *theScreen; /* the selected screen */
UBYTE PlaneMask = 0xFF; /* which bitplanes to use */
UBYTE theDepth; /* the depth of the bitmap */
WORD theWidth, theHeight; /* its width and height */
ULONG theModes; /* a copy of the screen's modes */
struct ColorMap *theColorMap; /* a copy of the screen's color map */
struct RastPort *theRastPort; /* the screen's RastPort */
struct BitMap theBitMap; /* the copied bitmap */
WORD StartX, StartY; /* position within the screen */
WORD CurrentX, CurrentY; /* current mouse position */
WORD LeftX, TopY; /* corner of selection rectangle */
extern struct MsgPort *CreatePort();
extern struct IOStdReq *CreateStdIO();
extern LONG AllocSignal(), Wait(), SetSignal();
extern APTR FindTask();
extern PLANEPTR AllocRaster();
extern struct ColorMap *GetColorMap();
extern APTR AllocMem();
extern void FrameHandlerStub();
#define INTUITION_REV 0L
#define LAYERS_REV 0L
#define GRAPHICS_REV 0L
#define EXIT_OK 0L
#define EXIT_ERROR 10L
#define STARTQUAL (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define NewX(i) (Frame->XY[i*2])
#define NewY(i) (Frame->XY[i*2+1])
#define OldX(i) (Frame->NextBorder->XY[i*2])
#define OldY(i) (Frame->NextBorder->XY[i*2+1])
#define SCREENX(x) (((x) >> Xshift) - theScreen->LeftEdge)
#define SCREENY(y) (((y) >> Yshift) - theScreen->TopEdge)
#define INTUIX(x) ((x) << Xshift)
#define INTUIY(y) ((y) << Yshift)
#define SCREENTOP\
(theScreen->TopEdge << ((theScreen->ViewPort.Modes & LACE)? 0: 1))
#define CTRL_C(e)\
((e)->ie_Code == 0x33 && ((e)->ie_Qualifier & IEQUALIFIER_CONTROL))
#define SIGBREAKF_ANY\
(SIGBREAKF_CTRL_C| SIGBREAKF_CTRL_D| SIGBREAKF_CTRL_E| SIGBREAKF_CTRL_F)
#define WIDTH ((long)theWidth)
#define HEIGHT ((long)theHeight)
#define DEPTH ((long)theDepth)
#define BLT_COPY 0x000000C0
#define BLT_ALLPLANES 0x000000FF
struct MsgPort *InputPort = NULL; /* Port used to talk to Input.Device */
struct IOStdReq *InputBlock = NULL; /* request block used with Input.Device */
static short InputOpen = FALSE; /* flag for when input.Device is open */
static APTR theTask; /* pointer to our task */
static LONG OkSignal; /* signal when movement detected */
static LONG EndSignal; /* signal when frame is done */
static LONG OkMask; /* 1 << OkSignal */
static LONG EndMask; /* 1 << EndSignal */
static short OkToSignal = TRUE; /* TRUE if not using shared memory */
static short MouseMoved = FALSE; /* TRUE when mouse moves buffered */
static short RemoveEvent; /* should an input event be removed? */
static short Xshift, Yshift; /* HIRES and LACE adjustments */
static WORD Xs,Ys, Xc, Yc; /* starting and current X,Y */
static WORD Hmin,Hmax, Wmin, Wmax; /* min and max movement values */
static SHORT NewBox[5][2]; /* current select frame */
static SHORT OldBox[5][2]; /* previous select frame */
struct Border NewFrame = {0,0, 0x01,0, COMPLEMENT, 5, &NewBox[0][0], NULL};
struct Border OldFrame = {0,0, 0x01,0, COMPLEMENT, 5, &OldBox[0][0], &NewFrame};
struct Border *Frame = &OldFrame;
static struct Interrupt HandlerData = /* used to add an input handler */
{
{NULL, NULL, 0, 52, NULL}, /* Node structure (nl_Pri = 52) */
NULL, /* data pointer */
&FrameHandlerStub /* code pointer */
};
/*
* GetMinMax()
*
* If a screen is selected,
* Get the min and max values from the screen
* Otherwise get them from Intuition itself
*/
static void GetMinMax()
{
if (theScreen)
{
Hmin = INTUIY(theScreen->TopEdge);
Hmax = INTUIY(theScreen->Height + theScreen->TopEdge - 1);
if (Hmax > IntuitionBase->MaxYMouse) Hmax = IntuitionBase->MaxYMouse;
Wmin = INTUIX(theScreen->LeftEdge);
Wmax = INTUIX(theScreen->Width + theScreen->LeftEdge - 1);
if (Wmax > IntuitionBase->MaxXMouse) Wmax = IntuitionBase->MaxXMouse;
} else {
Hmin = IntuitionBase->MinYMouse;
Hmax = IntuitionBase->MaxYMouse;
Wmin = IntuitionBase->MinXMouse;
Wmax = IntuitionBase->MaxXMouse;
}
}
/*
* DoSelectDown()
*
* Remove the event.
* Starting at the first screen find the first one higher than the mouse
* If none, choose the active screen, otherwise bring the screen to the front.
* Get the shift values from the screen's modes, and save the current X,Y
*/
static void DoSelectDown()
{
RemoveEvent = TRUE;
theScreen = IntuitionBase->FirstScreen;
while (theScreen && IntuitionBase->MouseY < SCREENTOP)
theScreen = theScreen->NextScreen;
if (theScreen == NULL)
theScreen = IntuitionBase->ActiveScreen;
else
ScreenToFront(theScreen);
Xshift = (theScreen->ViewPort.Modes & HIRES)? 0: 1;
Yshift = (theScreen->ViewPort.Modes & LACE)? 0: 1;
Xs = Xc;
Ys = Yc;
}
/*
* DoMouseMove()
*
* Get the mouse's movement and clip it to the movement rectangle.
* If right button is presseed and the select rectangle is started,
* Adjust the mouse movement, if necessary
* Move the starting position by this amount (ie, move the frame)
* If there was some movement,
* Don't remove the event,
* Pass on the modified mouse movements
* Adjust the mouse's current position
* If the select rectangle is already started, indicate that it has changed.
*/
static void DoMouseMove(theEvent)
struct InputEvent *theEvent;
{
register WORD dx,dy;
dx = theEvent->ie_X;
dy = theEvent->ie_Y;
if (dx < Wmin - Xc) dx = Wmin - Xc;
if (dy < Hmin - Yc) dy = Hmin - Yc;
if (dx > Wmax - Xc) dx = Wmax - Xc;
if (dy > Hmax - Yc) dy = Hmax - Yc;
if ((theEvent->ie_Qualifier & IEQUALIFIER_RBUTTON) && theScreen)
{
if (dx < Wmin - Xs) dx = Wmin - Xs;
if (dy < Hmin - Ys) dy = Hmin - Ys;
if (dx > Wmax - Xs) dx = Wmax - Xs;
if (dy > Hmax - Ys) dy = Hmax - Ys;
Xs += dx; Ys += dy;
}
if (dx != 0 || dy != 0)
{
RemoveEvent = FALSE;
theEvent->ie_X = dx;
theEvent->ie_Y = dy;
Xc += dx;
Yc += dy;
if (theScreen) MouseMoved = TRUE;
}
}
/*
* SignalMove()
*
* Mark that the shared memory is in use
* Save the start and current X,Y in the shared memory
* Signal the main task to draw the select rectangle
*
* (This stuff should all be replaced by message passing - I wrote this
* a long time ago before I had much experience)
*/
static void SignalMove()
{
OkToSignal = FALSE;
MouseMoved = FALSE;
StartX = SCREENX(Xs);
StartY = SCREENY(Ys);
CurrentX = SCREENX(Xc);
CurrentY = SCREENY(Yc);
Signal(theTask,OkMask);
}
/*
* FrameHandler()
*
* Get the current mouse position, and get the mouse movement rectangle.
* While there are more events to look at,
* Keep the event unless we say otherwise,
* Check the type of event:
* RAWMOUSE:
* Remove the event if the select box is up
* Check the button type:
* SELECTDOWN:
* If the select box is not already going, and the right qualifiers
* are present, then start the box
* SELECTUP:
* If the select box is going, signal that it's done
* MENU:
* Ignore these
* Anything else (i.e., MOUSEMOVE):
* Do a mouse move
* RAWKEY:
* If it is a CTRL-C, signal the task to stop.
* If we are removing the event, do so, otherwise go on to the next one.
* If mouse moves took placem and the memory is not in use, signal the move.
* Return the (modified) event list.
*/
struct InputEvent *FrameHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
register struct InputEvent **EventPtr = &EventList;
register struct InputEvent *theEvent;
Xc = IntuitionBase->MouseX;
Yc = IntuitionBase->MouseY;
GetMinMax();
Forbid();
while ((theEvent = *EventPtr) != NULL)
{
RemoveEvent = FALSE;
switch(theEvent->ie_Class)
{
case IECLASS_RAWMOUSE:
RemoveEvent = (theScreen != NULL);
switch(theEvent->ie_Code)
{
case SELECTDOWN:
if (theScreen == NULL &&
(theEvent->ie_Qualifier & STARTQUAL))
DoSelectDown();
break;
case SELECTUP:
if (theScreen) Signal(theTask,EndMask);
break;
case MENUDOWN:
case MENUUP:
break;
default:
DoMouseMove(theEvent);
break;
}
break;
case IECLASS_RAWKEY:
if (CTRL_C(theEvent)) Signal(theTask,SIGBREAKF_CTRL_C);
break;
}
if (RemoveEvent)
*EventPtr = theEvent->ie_NextEvent;
else
EventPtr = &(theEvent->ie_NextEvent);
}
Permit();
if (OkToSignal && MouseMoved) SignalMove();
return(EventList);
}
/*
* Ctrl_C()
*
* Dummy routine to disable Lattice-C CTRL-C trapping.
*/
#ifndef MANX
static int Ctrl_C()
{
return(0);
}
#endif
/*
* DoExit()
*
* General purpose exit routine. If 's' is not NULL, then print an
* error message with up to three parameters. Free any memory, close
* any open files, delete any ports, free any used signals, etc.
*/
void DoExit(s,x1,x2,x3)
char *s, *x1, *x2, *x3;
{
long status = EXIT_OK;
short i;
extern void DoUseExit();
if (theRastPort) UnlockLayers(&theScreen->LayerInfo);
if (s != NULL)
{
printf(s,x1,x2,x3);
printf("\n");
status = EXIT_ERROR;
}
DoUseExit();
for (i=0; i<theDepth; i++)
{
if (theBitMap.Planes[i])
FreeRaster(theBitMap.Planes[i],theWidth,theHeight);
}
if (InputOpen) CloseDevice(InputBlock);
if (InputBlock) DeleteStdIO(InputBlock);
if (InputPort) DeletePort(InputPort);
if (theColorMap) FreeColorMap(theColorMap);
if (OkSignal) FreeSignal(OkSignal);
if (EndSignal) FreeSignal(EndSignal);
if (IntuitionBase) CloseLibrary(IntuitionBase);
if (LayersBase) CloseLibrary(LayersBase);
if (GfxBase) CloseLibrary(GfxBase);
exit(status);
}
/*
* New()
*
* Get a chunk of memory and check that it has been allocated OK.
* If not, exit with an error message.
*/
APTR New(name,size)
char *name;
int size;
{
APTR ptr;
if ((ptr = AllocMem(size,MEMF_CLEAR | MEMF_PUBLIC)) == NULL)
DoExit("Can't Allocate Memory for %s",name);
return(ptr);
}
/*
* CheckLibOpen()
*
* General library open routine. It opens a library and sets a pointer
* to it. It checks that the library was openned successfully.
*/
void CheckLibOpen(lib,name,rev)
APTR *lib;
char *name;
int rev;
{
extern APTR OpenLibrary();
if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
DoExit("Can't open %s",name);
}
/*
* GetSignal()
*
* Allocate a signal (error if none available) and set the mask to
* the proper value.
*/
static void GetSignal(theSignal,theMask)
LONG *theSignal, *theMask;
{
LONG signal;
if ((signal = AllocSignal(-ONE)) == -ONE) DoExit("Can't Get Signal");
*theSignal = signal;
*theMask = (ONE << signal);
}
/*
* SetupTask()
*
* Find the task pointer for the main task (so the input handler can
* signal it). Clear the CTRL signal flags (so we don't get any left
* over from before JOURNAL was run) and allocate some signals for
* new events and errors (so the input handler can signal them).
*/
static void SetupTask()
{
theTask = FindTask(NULL);
SetSignal(0L,SIGBREAKF_ANY);
GetSignal(&OkSignal,&OkMask);
GetSignal(&EndSignal,&EndMask);
#ifndef MANX
onbreak(&Ctrl_C);
#endif
}
/*
* TellInputDevice()
*
* Create a port and a standard IO block for talking to the input device.
* Open the input device and tell it to add or remove the input handler.
* The handler is at priority 51, so it will be ahead of Intuition.
* Once the handler is added or removed, close the device and delete the
* request block and port.
*/
static void TellInputDevice(function)
long function;
{
long status;
if ((InputPort = CreatePort(0,0)) == NULL)
DoExit("Can't Create Port for Input Device");
if ((InputBlock = CreateStdIO(InputPort)) == NULL)
DoExit("Can't Create IO Block for Input Device");
if (OpenDevice("input.device",0,InputBlock,0) != 0)
DoExit("Can't Open Input Device");
InputOpen = TRUE;
InputBlock->io_Command = function;
InputBlock->io_Data = (APTR) &HandlerData;
if (status = DoIO(InputBlock)) DoExit("Error from DoIO: %ld",status);
CloseDevice(InputBlock); InputOpen = FALSE;
DeleteStdIO(InputBlock); InputBlock = NULL;
DeletePort(InputPort); InputPort = NULL;
}
/*
* DrawFrame()
*
* Set the corners of the border to their new positions.
* If the select frame has been moved,
* Get the old X,Y and calculate the new X,Y for the intermediate
* part of the border (the border is not square so as to avoid
* unnecessary flickering when it is sized)
* Update the intermediate border positions
* Otherwise
* Update the intermediate positions
& Draw the borders
* Switch which one is the current one, and link them properly
*/
static void DrawFrame()
{
WORD Xo,Yo, Xn,Yn;
NewX(3) = NewX(4) = StartX; NewX(1) = NewX(2) = CurrentX;
NewY(0) = NewY(1) = StartY; NewY(2) = NewY(3) = CurrentY;
if (StartX == OldX(4) && StartY == OldY(0))
{
Xo = OldX(2); Yo = OldY(2);
if ((CurrentX > StartX && StartX >= Xo) ||
(CurrentX < StartX && StartX <= Xo))
{
Xn = StartX;
} else {
if ((CurrentX > Xo && Xo > StartX) ||
(CurrentX < Xo && Xo < StartX))
Xn = Xo;
else
Xn = CurrentX;
}
if ((CurrentY > StartY && StartY >= Yo) ||
(CurrentY < StartY && StartY <= Yo))
{
Yn = StartY;
} else {
if ((CurrentY > Yo && Yo > StartY) ||
(CurrentY < Yo && Yo < StartY))
Yn = Yo;
else
Yn = CurrentY;
}
OldX(0) = NewX(0) = Xn;
OldY(4) = NewY(4) = Yn;
} else {
OldX(0) = OldX(4); OldY(4) = OldY(0);
NewX(0) = StartX; NewY(4) = StartY;
}
DrawBorder(theRastPort,Frame,0L,0L);
Frame->NextBorder->NextBorder = Frame;
Frame = Frame->NextBorder;
Frame->NextBorder->NextBorder = NULL;
}
/*
* CopyFrame()
*
* Get the mask for the planes actually needed
* Count the number of planes involved
* If none, give an error
* Get the widthm height, and upper-left-hand corner of the rectangle.
* Get a color map of the required depth and copy the screen's
* color map into it, skipping unneeded colors
* Get the screen's modes
* Initialize the BitMap, and a temporary one (to be used for blitting)
* For each plane selected
* Allocate a raster area for it (error if it not possible)
* Use it as the bitplane for both the TempBitMap and the BitMap
* (BitMap has them sequentially, TempBitMap has them in the positions
* corresponding to their positions in the screen bitmap)
* Clear the last two bytes of each bitplane (to make sure they are 0)
* Copy the screen data to the TempBitMap (and hence the to BitMap, since
* they share bitplanes)
*/
void CopyFrame()
{
short i,j,b;
UWORD *OldC,*NewC;
PLANEPTR thePlane;
UBYTE TempMask, NotPlaneMask;
struct BitMap TempBitMap;
long TempDepth = theScreen->BitMap.Depth;
PlaneMask &= (1 << TempDepth) - 1;
NotPlaneMask = ~PlaneMask;
for (TempMask = PlaneMask, theDepth = 0; TempMask; TempMask >>= 1)
theDepth += (TempMask & 1);
if (theDepth == 0) DoExit("No Bit Planes Selected By Mask");
if (CurrentX > StartX)
{
theWidth = CurrentX - StartX + 1;
LeftX = StartX;
} else {
theWidth = StartX - CurrentX + 1;
LeftX = CurrentX;
}
if (CurrentY > StartY)
{
theHeight = CurrentY - StartY + 1;
TopY = StartY;
} else {
theHeight = StartY - CurrentY + 1;
TopY = CurrentY;
}
theColorMap = GetColorMap((long)(1 << theDepth));
if (theColorMap == 0) DoExit("Can't Get Color Map");
OldC = (UWORD *)(theScreen->ViewPort.ColorMap->ColorTable);
NewC = (UWORD *)(theColorMap->ColorTable);
for (i=0; i<theScreen->ViewPort.ColorMap->Count; i++, OldC++)
if ((i & NotPlaneMask) == 0) *NewC++ = *OldC;
theModes = theScreen->ViewPort.Modes;
InitBitMap(&theBitMap,DEPTH,WIDTH,HEIGHT);
InitBitMap(&TempBitMap,TempDepth,WIDTH,HEIGHT);
for (TempMask=PlaneMask, i=j=0; TempMask; TempMask >>= 1,j++)
{
if (TempMask & 1)
{
thePlane = AllocRaster(WIDTH,HEIGHT);
if (thePlane == NULL) DoExit("Can't Get BitPlane %d",i);
TempBitMap.Planes[j] = theBitMap.Planes[i++] = thePlane;
for (b=RASSIZE(theWidth,theHeight)-1; b>0; b -= theBitMap.BytesPerRow)
thePlane[b] = thePlane[b-1] = 0;
}
}
BltBitMap(&theScreen->BitMap,(long)LeftX,(long)TopY,&TempBitMap,0L,0L,
WIDTH,HEIGHT,BLT_COPY,(long)PlaneMask,NULL);
}
/*
* TrackFrame()
*
* Setup the task, and get the signal mask
* Add the input handler and give the installation message
* Tell the user how to get the select rectangle
* While there is more to do,
* Wait for something to happen
* If we have gotten a new mouse position,
* If the screen is already chosen,
* Update and draw the new frame
* Otherwise,
* Get the rastport of the selected screen,
* Lock its layers so nothing happens while we're selecting
* Initialize the frames and draw them on the screen
* Indicate that we are done with the shared memory.
* If some other signal is present,
* End the loop
* Check if the loop was cancelled by the user
* If the select box was drawn,
* Finish the frame and remove it
* If not cancelled, get a copy of the frame
* Unlock the screen layers so things can happen again
* Forget the rastport
* Remove the handler
* return the status
*/
static int TrackFrame()
{
LONG signals;
LONG SigMask;
short NotDone = TRUE;
int NotCancelled = TRUE;
SetupTask();
SigMask = OkMask | EndMask | SIGBREAKF_CTRL_C;
TellInputDevice(IND_ADDHANDLER);
printf("%s %s Installed\n",program,version);
printf("Drag a selection rectangle while holding the SHIFT key. To change\n");
printf("the position of the box, press the right mouse button while dragging.\n");
printf("Press CTRL-C to cancel at any time.\n");
while (NotDone)
{
signals = Wait(SigMask);
if (signals & OkMask)
{
if (theRastPort)
{
DrawFrame();
} else {
theRastPort = &theScreen->RastPort;
LockLayers(&theScreen->LayerInfo);
OldX(0) = OldX(3) = OldX(4) = StartX; OldX(1) = OldX(2) = CurrentX;
OldY(0) = OldY(1) = OldY(4) = StartY; OldY(2) = OldY(3) = CurrentY;
DrawBorder(theRastPort,Frame->NextBorder,0L,0L);
}
OkToSignal = TRUE;
}
if (signals & ~OkMask)
{
NotDone = FALSE;
NotCancelled = ((signals & SIGBREAKF_CTRL_C) == 0);
if (theRastPort)
{
OldX(0) = OldX(4);
OldY(4) = OldY(0);
DrawBorder(theRastPort,Frame->NextBorder,0L,0L);
if (NotCancelled) CopyFrame();
UnlockLayers(&theScreen->LayerInfo);
theRastPort = NULL;
}
}
}
TellInputDevice(IND_REMHANDLER);
return(NotCancelled);
}
/*
* main()
*
* Parse the command-line arguments and perform the proper function
* (either show the usage, exit, or get a section of bitmap and use it).
*/
void main(argc,argv)
int argc;
char **argv;
{
int function;
extern int ParseArguments();
extern void UseFrame();
function = ParseArguments(argc,argv);
switch(function)
{
case SHOW_USAGE:
printf("Usage: %s\n",usage);
break;
case JUST_EXIT:
break;
default:
CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
CheckLibOpen(&LayersBase,"layers.library",LAYERS_REV);
CheckLibOpen(&GfxBase,"graphics.library",GRAPHICS_REV);
if (TrackFrame()) UseFrame(function);
break;
}
DoExit(NULL);
}